/**
 * @file		calendar.c
 * @brief		Calendar
 * @note		None
 * @attention	None
 * 
 * <B><I>ALL RIGHTS RESERVED, COPYRIGHT&copy; SOCIONEXT INCORPORATED 2016</I></B>
 */

/* Standard includes. */
#include <stdio.h>

#include "dd_pmc_calendar.h"
#include "calendar.h"

#if defined(BOARD_TYPE_SNI) || defined(BOARD_TYPE_SNI_SMALL)
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "event_groups.h"

#include "CMDinterpreter.h"

#include "dd_pmc_i2c.h"



static EventGroupHandle_t		xI2cEventFlag;

static T_DD_PMC_CALENDAR_DATA	xCalendarData;
#endif	// BOARD_TYPE_SNI || BOARD_TYPE_SNI_SMALL

static EventGroupHandle_t xRTCEventFlag;

#if defined(BOARD_TYPE_SNI) || defined(BOARD_TYPE_SNI_SMALL)
static T_DD_PMC_I2C_ACTION prvI2cCallback(E_DD_PMC_I2C_RECV xRecvAction, UCHAR ucRecvData)
{
	T_DD_PMC_I2C_ACTION	xRetAction;
	BaseType_t			xHigherPriorityTaskWoken, xResult;
	
	// Stop.
	Dd_PMC_I2C_Stop_Master();
	
	xHigherPriorityTaskWoken = pdFALSE;
	
	// Set Event Flag : I2C Send.
	xResult = xEventGroupSetBitsFromISR(xI2cEventFlag, 1, &xHigherPriorityTaskWoken);
	if (xResult == pdPASS) {
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}
	
	// Set Return value.
	xRetAction.action_mode	= 0;	// no action.
	xRetAction.send_data	= 0;	// no data.
	
	return xRetAction;
}

static int prvRTCStatus(E_DD_PMC_I2C_RW_MODE mode, unsigned char *pucStatus) {
	int							iRet = 0;
	T_DD_PMC_I2C_CTRL_MASTER	xCtrl;
	T_DD_PMC_I2C_START_INFO		xStartInfo;
	
	// paramter
	xCtrl.bps					= E_DD_PMC_I2C_BPS_400;
	xCtrl.dest_slave_addr_len	= E_DD_PMC_I2C_ADDR_LEN_7;
	xCtrl.dest_slave_addr		= 0x30;		// PT7C43390WEX(Slave:0x30) Command:000(Status Register 1)
	// set parameter
	iRet = Dd_PMC_I2C_Ctrl_Master(&xCtrl);
	if(iRet != D_DDIM_OK) {
		// result:NG
		return -1;
	}
	
	// Status Register 1
	xStartInfo.rw_mode		= mode;
	xStartInfo.rw_data_len	= 1;
	xStartInfo.rw_data		= pucStatus;
	// I2C Start
	Dd_PMC_I2C_Start_Master(&xStartInfo, (VP_PMC_I2C_CALLBACK)prvI2cCallback);
	if(iRet != D_DDIM_OK) {
		// I2C Stop
		Dd_PMC_I2C_Stop_Master();
		
		// result:NG
		return -1;
	}
	
	// wait
	xEventGroupWaitBits( xI2cEventFlag, 0x1, pdTRUE, pdFALSE, portMAX_DELAY );

	// I2C Stop
	Dd_PMC_I2C_Stop_Master();
	// result:OK
	return 0;
}
#endif	// BOARD_TYPE_SNI || BOARD_TYPE_SNI_SMALL

void prvSetRTCTime( void ) {
#if defined(BOARD_TYPE_SNI) || defined(BOARD_TYPE_SNI_SMALL)
	int							iRet = 0;
	int							iLoop;
	unsigned char				ucStatus = 0;
	unsigned char				ucCalendar[7];
	unsigned char				ucRealTimeData1[7];
	T_DD_PMC_I2C_CTRL_MASTER	xCtrl;
	T_DD_PMC_I2C_START_INFO		xStartInfo;
	
	// Event Flag
	if( NULL == xI2cEventFlag ) {
		return;
	}
	
	// Get RTC Status
	iRet = prvRTCStatus(E_DD_PMC_I2C_RW_MODE_READ, &ucStatus);
	// Status check
	if((iRet != 0) || ((ucStatus & 0x01) == 0x01) || ((ucStatus & 0x02) == 0x02)) {
		return;
	}
	// 0.5s wait.
	vTaskDelay( 500 );
	
	// set 24-hour mode.
	ucStatus |= 0x40;
	iRet = prvRTCStatus(E_DD_PMC_I2C_RW_MODE_WRITE, &ucStatus);
	if(iRet != 0) {
		return;
	}
	
	// 0.5s wait.
	vTaskDelay( 500 );
	
	// Get RTC Status
	iRet = prvRTCStatus(E_DD_PMC_I2C_RW_MODE_READ, &ucStatus);
	// Status check
	if((iRet != 0) || ((ucStatus & 0x01) == 0x01) || ((ucStatus & 0x02) == 0x02)) {
		return;
	}
	
	// Convert RTC Time Info
	ucCalendar[0] = xCalendarData.year;			// Year
	ucCalendar[1] = xCalendarData.month;		// Month
	ucCalendar[2] = xCalendarData.day;			// Day
	ucCalendar[4] = xCalendarData.hour;			// Hour
	ucCalendar[5] = xCalendarData.minute;		// Minute
	ucCalendar[6] = xCalendarData.second;		// Second
	
	// Day of week
	if (xCalendarData.day_of_week == 6) {
		ucCalendar[3] = 0;
	}
	else {
		ucCalendar[3] = xCalendarData.day_of_week + 1;
	}
	
	for( iLoop = 0; iLoop < 7; iLoop++ ) {
		ucRealTimeData1[iLoop] = 0;
		
		if (ucCalendar[iLoop] >= 80) {
			ucRealTimeData1[iLoop] |= 0x01;
			ucCalendar[iLoop] -= 80;
		}
		if (ucCalendar[iLoop] >= 40) {
			ucRealTimeData1[iLoop] |= 0x02;
			ucCalendar[iLoop] -= 40;
		}
		if (ucCalendar[iLoop] >= 20) {
			ucRealTimeData1[iLoop] |= 0x04;
			ucCalendar[iLoop] -= 20;
		}
		if (ucCalendar[iLoop] >= 10) {
			ucRealTimeData1[iLoop] |= 0x08;
			ucCalendar[iLoop] -= 10;
		}
		if (ucCalendar[iLoop] >= 8) {
			ucRealTimeData1[iLoop] |= 0x10;
			ucCalendar[iLoop] -= 8;
		}
		if (ucCalendar[iLoop] >= 4) {
			ucRealTimeData1[iLoop] |= 0x20;
			ucCalendar[iLoop] -= 4;
		}
		if (ucCalendar[iLoop] >= 2) {
			ucRealTimeData1[iLoop] |= 0x40;
			ucCalendar[iLoop] -= 2;
		}
		if (ucCalendar[iLoop] >= 1) {
			ucRealTimeData1[iLoop] |= 0x80;
		}
	}
	
	// Set RTC Time Info
	// paramter
	xCtrl.bps					= E_DD_PMC_I2C_BPS_400;
	xCtrl.dest_slave_addr_len	= E_DD_PMC_I2C_ADDR_LEN_7;
	xCtrl.dest_slave_addr		= 0x32;		// PT7C43390WEX(Slave:0x30) Command:010(Real-time data 1)
	// set parameter
	iRet = Dd_PMC_I2C_Ctrl_Master(&xCtrl);
	if(iRet != D_DDIM_OK) {
		return;
	}
	
	// Write - Real-time data 1
	xStartInfo.rw_mode		= E_DD_PMC_I2C_RW_MODE_WRITE;
	xStartInfo.rw_data_len	= 7;
	xStartInfo.rw_data		= ucRealTimeData1;
	// I2C Start
	iRet = Dd_PMC_I2C_Start_Master(&xStartInfo, (VP_PMC_I2C_CALLBACK)prvI2cCallback);
	if(iRet == D_DDIM_OK) {
		// wait
		xEventGroupWaitBits( xI2cEventFlag, 0x1, pdTRUE, pdFALSE, portMAX_DELAY );
	}
	
	// I2C Stop
	Dd_PMC_I2C_Stop_Master();
	Dd_PMC_I2C_Terminate();
#endif	// BOARD_TYPE_SNI || BOARD_TYPE_SNI_SMALL
}

/**
Calendar Initialization.
*/
void vCalendarInit(void)
{
#if defined(BOARD_TYPE_SNI) || defined(BOARD_TYPE_SNI_SMALL)
	// RTC setting
	int							iRet = 0;
	int							iLoop;
	unsigned char				ucStatus = 0;
	unsigned char				ucRealTimeData1[7];
	T_DD_PMC_I2C_CTRL_MASTER	xCtrl;
	T_DD_PMC_I2C_START_INFO		xStartInfo;
	T_DD_PMC_CALENDAR_DATA		xCalendar;
	int							iCalendarData[7];
	
	// Event Flag
	xI2cEventFlag = xEventGroupCreate();
	if( NULL == xI2cEventFlag ) {
		// Calendar initialize(set default time).
		Dd_PMC_Calendar_Init();
		
		return;
	}
	
	// RTC Status Check (retry count:100)
	for ( iLoop = 0; iLoop < 100; iLoop++ ) {
		// Get RTC Status(Status Register 1)
		iRet = prvRTCStatus(E_DD_PMC_I2C_RW_MODE_READ, &ucStatus);
		
		if((iRet != 0) || ((ucStatus & 0x01) == 0x01) || ((ucStatus & 0x02) == 0x02)) {
			// 0.5s wait.
			vTaskDelay( 500 );
			
			// Status Register 1 - RESET on
			ucStatus = 0x80;
			// Set RTC Status(Status Register 1)
			iRet = prvRTCStatus(E_DD_PMC_I2C_RW_MODE_WRITE, &ucStatus);
		}
		else {
			// Checked
			break;
		}
	}

	// retry count max
	if (iLoop == 100) {
		// Calendar initialize(set default time).
		Dd_PMC_Calendar_Init();
		
		return;
	}
	// Get RTC Time Info
	// paramter
	xCtrl.bps					= E_DD_PMC_I2C_BPS_400;
	xCtrl.dest_slave_addr_len	= E_DD_PMC_I2C_ADDR_LEN_7;
	xCtrl.dest_slave_addr		= 0x32;		// PT7C43390WEX(Slave:0x30) Command:010(Real-time data 1)
	// set parameter
	iRet = Dd_PMC_I2C_Ctrl_Master(&xCtrl);
	if(iRet != D_DDIM_OK) {
		// Calendar initialize(set default time).
		Dd_PMC_Calendar_Init();
		
		return;
	}
	
	// Read - Real-time data 1
	xStartInfo.rw_mode		= E_DD_PMC_I2C_RW_MODE_READ;
	xStartInfo.rw_data_len	= 7;
	xStartInfo.rw_data		= ucRealTimeData1;
	// Send start
	iRet = Dd_PMC_I2C_Start_Master(&xStartInfo, (VP_PMC_I2C_CALLBACK)prvI2cCallback);
	if(iRet == D_DDIM_OK) {
		// wait
		xEventGroupWaitBits( xI2cEventFlag, 0x1, pdTRUE, pdFALSE, portMAX_DELAY );
		
		// Analize Real-time data 1
		for( iLoop = 0; iLoop < 7; iLoop++ ) {
			// data clear
			iCalendarData[iLoop] = 0;
			
			// Bit7 : Y1/M1/D1/W1/H1/m1/s1
			if((ucRealTimeData1[iLoop] & 0x80) == 0x80) {
				iCalendarData[iLoop] += 1;
			}
			// Bit6 : Y2/M2/D2/W2/H2/m2/s2
			if((ucRealTimeData1[iLoop] & 0x40) == 0x40) {
				iCalendarData[iLoop] += 2;
			}
			// Bit5 : Y4/M4/D4/W4/H4/m4/s4
			if((ucRealTimeData1[iLoop] & 0x20) == 0x20) {
				iCalendarData[iLoop] += 4;
			}
			// Bit4 : Y10/M10/D10/H10/m10/s10
			if((ucRealTimeData1[iLoop] & 0x10) == 0x10) {
				iCalendarData[iLoop] += 8;
			}
			// Bit3 : Y10/M10/D10/H10/m10/s10
			if((ucRealTimeData1[iLoop] & 0x08) == 0x08) {
				iCalendarData[iLoop] += 10;
			}
			// Bit2 : Y20/D20/H20/m20/s20
			if((ucRealTimeData1[iLoop] & 0x04) == 0x04) {
				iCalendarData[iLoop] += 20;
			}
			// Bit1 : Y40/H(AM/PM)/m40/s40
			if((ucRealTimeData1[iLoop] & 0x02) == 0x02) {
				if(iLoop != 4) {
					iCalendarData[iLoop] += 40;
				}
			}
			// Bit0 : Y80
			if((ucRealTimeData1[iLoop] & 0x01) == 0x01) {
				iCalendarData[iLoop] += 80;
			}
		}
		
		if (iCalendarData[3] != 0) {
			iCalendarData[3]--;
		}
		else {
			iCalendarData[3] = 6;
		}
		
		// Set Calendar data.
		xCalendar.second		= iCalendarData[6];
		xCalendar.minute		= iCalendarData[5];
		xCalendar.hour			= iCalendarData[4];
		xCalendar.day			= iCalendarData[2];
		xCalendar.day_of_week	= iCalendarData[3];
		xCalendar.month			= iCalendarData[1];
		xCalendar.year			= iCalendarData[0];
		Dd_PMC_Calendar_Set_Calendar(&xCalendar);
	}
	else {
		// Initialize(set default time).
		Dd_PMC_Calendar_Init();
	}
	
	// I2C Stop
	Dd_PMC_I2C_Stop_Master();
	Dd_PMC_I2C_Terminate();
#else	// BOARD_TYPE_SNI || BOARD_TYPE_SNI_SMALL
	// Initialize.
	Dd_PMC_Calendar_Init();
#endif	// BOARD_TYPE_SNI || BOARD_TYPE_SNI_SMALL
}

/**
set Date and time.
@param [in]		cmd		Command data (SET Calendar data).<br>
@retval  0 : success
@retval  1 : parameter error
*/
int iCalendarSetDate(unsigned *cmd)
{
	int ret = 0;
	T_DD_PMC_CALENDAR_DATA	calendar;
	
	// Set command parameter.
	calendar.second			= cmd[2];
	calendar.minute			= cmd[3];
	calendar.hour			= cmd[4];
	calendar.day			= cmd[5];
	calendar.day_of_week	= cmd[6];
	calendar.month			= cmd[7];
	calendar.year			= cmd[8];
	
	// Set Calendar data.
	ret = Dd_PMC_Calendar_Set_Calendar(&calendar);
	if (ret != 0)
	{
		return -1;
	}
	
	return 0;
}

/**
get Date and time.
@param [out]	cmd		Command data (GET Calendar data).<br>
*/
void vCalendarGetDate(unsigned *cmd)
{
	T_DD_PMC_CALENDAR_DATA	calendar;
	
	// Get Calendar data.
	Dd_PMC_Calendar_Get_Calendar(&calendar);
	
	// Set retrun value.
	cmd[2] = calendar.second;
	cmd[3] = calendar.minute;
	cmd[4] = calendar.hour;
	cmd[5] = calendar.day;
	cmd[6] = calendar.day_of_week;
	cmd[7] = calendar.month;
	cmd[8] = calendar.year;
}

/**
set Date and time to External RTC.
@param [in]		cmd		Command data (SET RTC data).<br>
@retval  0 : success
@retval  1 : parameter error
*/
int iCalendarSetExtRTC(unsigned *cmd)
{
	int							iRet = 0;
	BaseType_t					xHigherPriorityTaskWoken, xResult;

	// Set command parameter.
	xCalendarData.second		= cmd[2];
	xCalendarData.minute		= cmd[3];
	xCalendarData.hour			= cmd[4];
	xCalendarData.day			= cmd[5];
	xCalendarData.day_of_week	= cmd[6];
	xCalendarData.month			= cmd[7];
	xCalendarData.year			= cmd[8];
	
	// Set Calendar data.
	iRet = Dd_PMC_Calendar_Set_Calendar(&xCalendarData);
	if (iRet != 0)
	{
		return -1;
	}

	xHigherPriorityTaskWoken = pdFALSE;
	
	// Set Event Flag : Set RTC Time.
	xResult = xEventGroupSetBitsFromISR(xRTCEventFlag, 1, &xHigherPriorityTaskWoken);
	if (xResult == pdPASS) {
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}
	
	return 0;
}

void vRTCTask( void *pvParameters )
{
	xRTCEventFlag = xEventGroupCreate();
	if( NULL == xRTCEventFlag ) {
		// error
		for(;;);
	}
	
	for(;;) {
		xEventGroupWaitBits( xRTCEventFlag, 0x1, pdTRUE, pdFALSE, portMAX_DELAY );

		// Set RTC Time
		prvSetRTCTime();
	}
}
